package org.piwicode.bywordofmouth;

import com.google.common.base.Preconditions;
import com.google.common.collect.ImmutableCollection;
import com.google.common.collect.ImmutableList;
import java.io.File;
import java.io.IOException;
import java.io.Serializable;
import java.net.URI;
import java.nio.file.FileVisitResult;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.SimpleFileVisitor;
import java.nio.file.attribute.BasicFileAttributes;
import java.util.Deque;
import java.util.LinkedList;
import java.util.logging.Level;
import java.util.logging.Logger;

/**
 *
 * @author pierre
 */
public class Directory implements NamedObject, Resource, Serializable {

    private static final Logger LOG = Logger.getLogger(Directory.class.getName());
    private final String name;
    private final URI uri;
    private final Resource root;

    public Directory(String name, URI uri, Resource root) {
        this.name = Preconditions.checkNotNull(name);
        this.uri = Preconditions.checkNotNull(uri);
        this.root = Preconditions.checkNotNull(root);
    }

    public void processFiles(final Resource rootResource, final ImmutableList.Builder<Resource> resBldr, final ImmutableCollection.Builder<File> fileBldr) throws IOException {

        LOG.log(Level.INFO, "Scanning file system at {0} started", uri);
        final Deque<Resource> deq = new LinkedList<>();
        Files.walkFileTree(Paths.get(uri), new SimpleFileVisitor<Path>() {
            @Override
            public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) throws IOException {
                if (deq.isEmpty()) {
                    deq.add(Directory.this);
                } else {
                    deq.add(new FileResource(deq.getLast(), dir.getFileName().toString(), -1L, -1L));
                }
                return FileVisitResult.CONTINUE;
            }

            @Override
            public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
                if (!attrs.isRegularFile()) {
                    return FileVisitResult.CONTINUE;
                }
                final String fileName = file.getFileName().toString();
                resBldr.
                        add(new FileResource(deq.getLast(), fileName, attrs.lastModifiedTime().toMillis(), attrs.size()));
                fileBldr.add(file.toFile());
                return FileVisitResult.CONTINUE;
            }

            @Override
            public FileVisitResult postVisitDirectory(Path dir, IOException exc) throws IOException {
                if (exc == null) {
                    resBldr.add(deq.removeLast());
                }
                return FileVisitResult.CONTINUE;
            }

            @Override
            public FileVisitResult visitFileFailed(Path file, IOException exc) throws IOException {
                //Ignore errors
                return FileVisitResult.CONTINUE;
            }
        });
        LOG.log(Level.INFO, "Scanning file system at {0} completed", uri);
    }

    @Override
    public String name() {
        return name;
    }

    @Override
    public boolean isDirectory() {
        return true;
    }

    @Override
    public long lastModified() {
        return -1;
    }

    @Override
    public long length() {
        return -1;
    }

    @Override
    public Path path() {
        return Paths.get(uri);
    }

    @Override
    public Resource parent() {
        return root;
    }

    @Override
    public String toString() {
        return root.toString() + "/" + name;
    }
}
